﻿using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
namespace WITD
{
    /// <summary>
    /// A Window class for adding a new <see cref="WorkItemLinkType">WorkItemLinkType</see> to the collection.
    /// </summary>
    public partial class AddNewLinkTypeForm : Window
    {

        private WorkItemStore Store;
        private WorkItemStore[] Stores;
        private ArrayList Topologies = new ArrayList();
        private String CurrentTopology;
        private WITLinkType LinkManager;
        private String CurrentLinkType;
        Opaque hider;
        Window toolWindow;
        bool IsDirty = false;

        String _referenceName;
        String _reverseName;
        String _forwardName;
        String _topology;

        /// <summary>
        /// Constructor for the class. Instantiates a form for adding a new link type or removing an existing link type.
        /// </summary>
        /// <param name="store">The work item store instance to inspect.</param>
        /// <param name="parent"></param>
        public AddNewLinkTypeForm(WorkItemStore store, Window parent)
        {
            if (store == null)
                throw new ArgumentNullException("store", "The store argument of type WorkItemStore cannot be null.");
            
            InitializeComponent();

            lblStores.Visibility = System.Windows.Visibility.Hidden;
            cboStores.Visibility = System.Windows.Visibility.Hidden;
            this.Title +=  " for collection: " + store.TeamProjectCollection.Uri.ToString();
            Store = store;
            LinkManager = new WITLinkType();
            Owner = parent;

            PopulateTopologies(Store);
            PopulateLinkTypes(Store);

            txtLinkForwardName.Focus();

            hider = new Opaque(this);

            IsDirty = false;
        }

        /// <summary>
        /// Fills the combo box with the topologies available for the current Team Foundation WorkItemStore.
        /// </summary>
        /// <param name="store">The <see cref="WorkItemStore">WorkItemStore</see> instance to work with.</param>
        private void PopulateTopologies(WorkItemStore store)
        {
            foreach (string item in GetTopologies(store))
            {
                if (item.ToUpper() != "UNKNOWN")
                {
                    cboTopology.Items.Add(item);
                }
            }

            if (cboTopology.HasItems)
            {
                cboTopology.SelectedIndex = 0;
            }

            cboTopology.Items.SortDescriptions.Add(new System.ComponentModel.SortDescription());
        }

        /// <summary>
        /// Retrieves the available link topologies from the specified WorkItemStore.
        /// </summary>
        /// <param name="store">The <see cref="WorkItemStore">WorkItemStore</see> instance to work with..</param>
        /// <returns>A string array of available link topologies.</returns>
        private string[] GetTopologies(WorkItemStore store)
        {
            WorkItemLinkType.Topology top = WorkItemLinkType.Topology.Dependency;
            return Enum.GetNames(top.GetType());
        }

        /// <summary>
        /// Populates the Items list in the Link types ListBox.
        /// </summary>
        /// <param name="store">The WorkItemStore to retrieve link types from.</param>
        private void PopulateLinkTypes(WorkItemStore store)
        {
            if (lstLinks.Items.Count > 0) { lstLinks.Items.Clear(); }
            foreach (WorkItemLinkType link in store.WorkItemLinkTypes)
            {
                lstLinks.Items.Add(link.ReferenceName);
            }

            lstLinks.Items.SortDescriptions.Add(new System.ComponentModel.SortDescription());
        }

        /// <summary>
        /// Constructor the <see cref="AddNewLinkTypeForm">AddNewLinkTypeForm</see> Window class./>
        /// </summary>
        /// <param name="stores">Accepts a collection of WorkItemStores.</param>
        /// <param name="parent">The owning Window object instance. It must have been shown at least once to avoid an Exception.</param>
        public AddNewLinkTypeForm(WorkItemStore[] stores, Window parent)
        {
            if (stores == null || stores.Length == 0)
                throw new ArgumentNullException("store", "The stores argument of type WorkItemStore[] cannot be null or zero length.");

            InitializeComponent();
            lblStores.Visibility = System.Windows.Visibility.Visible;
            cboStores.Visibility = System.Windows.Visibility.Visible;
            LinkManager = new WITLinkType();
            Stores = stores;

            foreach (WorkItemStore store in Stores)
            {
                cboStores.Items.Add(store.TeamProjectCollection.Name);
            }
            WorkItemLinkType.Topology top = WorkItemLinkType.Topology.Dependency;

            string[] topologies = Enum.GetNames(top.GetType());

            foreach (string item in topologies)
            {
                if (item.ToUpper() != "UNKNOWN")
                {
                    cboTopology.Items.Add(item);
                }
            }

            if (cboTopology.HasItems)
            {
                cboTopology.SelectedIndex = 0;
            }

            Owner = parent;
            hider = new Opaque(this);
        }

        /// <summary>
        /// Stores the currently selected topology from the cboTopology combobox.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cboTopology_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (cboTopology.SelectedItem != null)
            {
                if (CurrentTopology != cboTopology.SelectedItem.ToString())
                {
                    IsDirty = true;
                    CurrentTopology = cboTopology.SelectedItem.ToString();
                }
            }
        }

        /// <summary>
        /// Sets the mouse cursor to the specified cursor.
        /// </summary>
        /// <param name="newCursor">The new cursor.</param>
        private void SetCursor(Cursor newCursor)
        {
            this.Cursor = newCursor;
        }

        /// <summary>
        /// Saves the link type, optionally updating an existing link type definition.
        /// </summary>
        private void Save()
        {
            //must have at least one . (dot) in Referencename
            if (!String.IsNullOrEmpty(txtLinkRefName.Text) && txtLinkRefName.Text.Contains("."))
            {
                if (!String.IsNullOrEmpty(txtLinkForwardName.Text))
                {
                    if (!String.IsNullOrEmpty(txtLinkReverseName.Text))
                    {
                        if (!String.IsNullOrEmpty(CurrentTopology))
                        {
                            try
                            {
                                //we're good
                                ObscureMainWindow(null);
                                CreateNewLink(txtLinkRefName.Text, txtLinkForwardName.Text, txtLinkReverseName.Text, CurrentTopology, chkUpdateExisting.IsChecked.Value);
                                IsDirty = false;
                            }
                            finally
                            {
                                UnobscureMainWindow();
                            }
                        }
                        else
                        {
                            MessageBox.Show("Error: The topology is not selected.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                        }
                    }
                    else
                    {
                        MessageBox.Show("Error: The reverse name value is invalid.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                }
                else
                {
                    MessageBox.Show("Error: The forward name value is invalid.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            else
            {
                MessageBox.Show("Error: The reference name value is invalid.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// Calls method <see cref="CreateNewLink">CreateNewLink()</see> to create the new link type./>
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSave_Click(object sender, RoutedEventArgs e)
        {
            if (!IsDirty)
            {
                MessageBox.Show("Data about this link type has not changed.", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }
            else
                Save();            
        }

        /// <summary>
        /// Attempts to add a new link type with the values provided on the form.
        /// </summary>
        /// <param name="referenceName">The reference name for the new link type.</param>
        /// <param name="forwardName">The forward name for the new link type.</param>
        /// <param name="reverseName">The reverse name for the new link type.</param>
        /// <param name="topology">The link topology for the new link type.</param>
        private void CreateNewLink(String referenceName, String forwardName, String reverseName, String topology, bool updateExisting)
        {
            bool success = false;
            String message = "";

            try
            {
                SetCursor(System.Windows.Input.Cursors.Wait);
                SetButtons(false);

                success = LinkManager.AddLinkType(Store, referenceName, forwardName, reverseName, topology, updateExisting, out message);
                if (!success)
                {
                    ShowError(message, System.Reflection.MethodInfo.GetCurrentMethod().Name);
                }
                else
                {
                    Sync();
                    PopulateLinkTypes(Store);
                    txtLinkForwardName.Clear();
                    txtLinkRefName.Clear();
                    txtLinkReverseName.Clear();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: " + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                SetCursor(Cursors.Arrow);
                SetButtons(true);
            }
        }

        /// <summary>
        /// Captures the currently selected link type in the <see cref="lstLinks">lstLinks</see> ListBox.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lstLinks_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (lstLinks.SelectedItem != null)
            {
                CurrentLinkType = lstLinks.SelectedItem.ToString();
            }
            
        }

        /// <summary>
        /// Sets the Window's buttons .IsEnabled property to the specified attributeValue.
        /// </summary>
        /// <param name="enabled">True to enable the Window's buttons. Otherwise false;</param>
        private void SetButtons(bool enabled)
        {
            btnCancel.IsEnabled = enabled;
            btnHelp.IsEnabled = enabled;
            btnRemoveLink.IsEnabled = enabled;
            btnSave.IsEnabled = enabled;
        }

        /// <summary>
        /// Occurs when the user clicks the Remove Link button. calls the RemoveLink method.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRemoveLink_Click(object sender, RoutedEventArgs e)
        {
            if (!String.IsNullOrEmpty(CurrentLinkType))
            {
                if (MessageBox.Show(String.Format((string)Application.Current.FindResource("PromptRemoveLinkType"), CurrentLinkType), "Verify removal", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
                {
                    RemoveLink(Store, CurrentLinkType);
                }
            }
        }

        /// <summary>
        /// Attempts to remove the specified link type from the specified collection.
        /// </summary>
        /// <param name="store">The WorkItemStore from which to remove the specified link type.</param>
        /// <param name="linkTypeName">The reference name of the link type to remove.</param>
        private void RemoveLink(WorkItemStore store, String linkTypeName)
        {
            bool success = true;
            string message;

            try
            {
                SetCursor(System.Windows.Input.Cursors.Wait);
                SetButtons(false);
                ObscureMainWindow(null);

                success = LinkManager.RemoveLinkType(Store, linkTypeName, out message);
                if (!success)
                {
                    ShowError(message, System.Reflection.MethodInfo.GetCurrentMethod().Name);
                }
                else
                {
                    PopulateLinkTypes(Store);
                }
            }
            catch (Exception ex)
            {
                success = false;
                MessageBox.Show("Error: " + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                UnobscureMainWindow();
                SetButtons(true);
                SetCursor(Cursors.Arrow);
            }
        }

        /// <summary>
        /// Cancels adding a new link type and closes this window.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            MessageBoxResult answer = MessageBoxResult.None;
            SetCursor(Cursors.Arrow);
            if (IsDirty)
            {
                answer = MessageBox.Show("You have unsaved changes. Do you want to save your changes first?", "Unsaved changes", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
                switch (answer)
                {
                    case MessageBoxResult.Yes:
                            Save();
                            this.Close();
                            break;

                    case MessageBoxResult.No:
                            this.Close();
                            break;

                    case MessageBoxResult.Cancel:
                            return;
                }
            }
            else
                Close();
        }

        /// <summary>
        /// Opens a viewer to display information on link type topologies.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnHelp_Click(object sender, RoutedEventArgs e)
        {
            TopologiesHelp helpViewer = new TopologiesHelp(hider);
            ObscureMainWindow(helpViewer);
            SetButtons(false);
            helpViewer.ShowDialog();
            UnobscureMainWindow();
            SetButtons(true);
        }

        /// <summary>
        /// Displays an error message if specified
        /// </summary>
        /// <param name="message">Optionally has an error message.</param>
        /// <param name="caller">The name of method that experienced the error.</param>
        private void ShowError(String message, String caller)
        {
            if (!String.IsNullOrEmpty(message))
            {
                MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            else
            {
                MessageBox.Show("Error in " + caller + ".", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// Displays a MessageBox with the information about the currently selected link type.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnShowInfo_Click(object sender, RoutedEventArgs e)
        {
            if (!String.IsNullOrEmpty(CurrentLinkType))
            {
                ShowLinkInfo(CurrentLinkType);
            }
        }

        /// <summary>
        /// Displays information about the specified link type.
        /// </summary>
        /// <param name="linkName">The reference name of the link type from which to display information.</param>
        private void ShowLinkInfo(String linkName)
        {
            String message;

            WorkItemLinkType link = LinkManager.GetLinkInfo(Store, linkName, out message);

            if (link != null)
            {
                MessageBox.Show("Link information for " + CurrentLinkType + ":                          " + Environment.NewLine + 
                                "Reference name:      " + link.ReferenceName + Environment.NewLine +
                                "Forward name:          " + link.ForwardEnd.Name + Environment.NewLine +
                                "Reverse name:          " + link.ReverseEnd.Name + Environment.NewLine +
                                "Topology:                  " + link.LinkTopology.ToString(), "Link type info:    " + CurrentLinkType,
                                MessageBoxButton.OK, MessageBoxImage.Information);

            }
            else if (!String.IsNullOrEmpty(message))
            {
                ShowError(message, System.Reflection.MethodInfo.GetCurrentMethod().Name);
            }
        }

        /// <summary>
        /// Places an opaque window over the current window to focus the user's attention on the new form about to open and claim focus.
        /// </summary>
        private void ObscureMainWindow(Window newWindow)
        {
            Topmost = false;
            toolWindow = newWindow;
            if (newWindow != null) newWindow.Topmost = true;
            hider.Height = this.Height;
            hider.Width = this.Width;
            hider.Top = this.Top;
            hider.Left = this.Left;
            hider.Cursor = Cursors.Wait;
            hider.Show();
        }

        /// <summary>
        /// Hides the opaque window without destroying it, making the current form the focus once again.
        /// </summary>
        private void UnobscureMainWindow()
        {
            hider.Hide();
            toolWindow = this;
            this.Topmost = true;
            this.Focus();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Activated(object sender, EventArgs e)
        {
            if (hider.Visibility == System.Windows.Visibility.Visible)
            {
                hider.Topmost = true;
                if (toolWindow != null)
                {
                    toolWindow.Topmost = true;
                    toolWindow.Focus();
                }
            }
        }

        /// <summary>
        /// Fills in the details of the specified link type on the form.
        /// </summary>
        /// <param name="currentLinkType">The name of the link type of which to fill in the details. </param>
        private void PreFill(String currentLinkType)
        {
            String message;

            WorkItemLinkType link = LinkManager.GetLinkInfo(Store, CurrentLinkType, out message);

            if (link != null)
            {
                txtLinkForwardName.Text = link.ForwardEnd.Name;
                txtLinkRefName.Text = link.ReferenceName;
                txtLinkReverseName.Text = link.ReverseEnd.Name;
                cboTopology.SelectedItem = link.LinkTopology.ToString();
                IsDirty = false;
            }
            else if (!String.IsNullOrEmpty(message))
            {
                ShowError(message, System.Reflection.MethodInfo.GetCurrentMethod().Name);
            }
         }

        /// <summary>
        /// Prefills the text boxes with the selected link type details.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPreFill_Click(object sender, RoutedEventArgs e)
        {
            if (String.IsNullOrEmpty(CurrentLinkType))
            {
                throw new ArgumentNullException("currentLinkType", "currentLinkType cannot be null or empty.");
            }

            PreFill(CurrentLinkType);
        }

        /// <summary>
        /// Attempts to refresh the cache for the current WorkItemStore and after 5 seconds, syncs the client to the cached data.
        /// <para></para>
        /// If the SyncReconnect fails, then a reconnect to the current project occurs to get the updated project information.
        /// </summary>
        private void Sync()
        {
            SetCursor(Cursors.Wait);
            System.Threading.AutoResetEvent ev = new System.Threading.AutoResetEvent(false);

            Store.RefreshCache();
            ev.WaitOne(5000);
            Store.SyncToCache();
            SetCursor(Cursors.Arrow);
        }

        /// <summary>
        /// Sets the form as dirty.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtLinkForwardName_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (_forwardName != txtLinkForwardName.Text)
            {
                IsDirty = true;
                _forwardName = txtLinkForwardName.Text.Trim();
                IsDirty = !String.IsNullOrEmpty(_forwardName);
            }
        }

        /// <summary>
        /// Sets the form as dirty.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtLinkReverseName_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (_reverseName != txtLinkReverseName.Text)
            {
                IsDirty = true;
                _reverseName = txtLinkReverseName.Text.Trim();
                IsDirty = !String.IsNullOrEmpty(_reverseName);
            }
        }

        /// <summary>
        /// Sets the form as dirty.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtLinkRefName_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (_referenceName != txtLinkRefName.Text)
            {
                IsDirty = true;
                _referenceName = txtLinkRefName.Text.Trim();
                IsDirty = !String.IsNullOrEmpty(_referenceName);
            }
        }

        /// <summary>
        /// Prefills the text boxes with the discrete link values of the clicked link type.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lstLinks_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (lstLinks.SelectedItem != null)
            {
                CurrentLinkType = lstLinks.SelectedItem.ToString();
                PreFill(CurrentLinkType);
            }
        }
    }
}
